<?php
/**
 * Created by PhpStorm.
 * User: Qch
 * Date: 2016/8/15
 * Time: 20:11
 */

namespace Aoe\Core;

use Aoe\Attributes\Container\Dependent;
use Aoe\Intent\Request\Request;
use Aoe\Util\Bus\Accessor;
use Aoe\Util\Bus\Config\Configurable;
use Aoe\Util\Bus\Store\Obj;
use Aoe\Util\Exception;
use Aoe\Util\Logger\NameRecorder;
use Aoe\Util\Manager\ClosureCollection;
use ReflectionClass;
use Throwable;

/**
 * # 模块类
 *   * 通过kernel::getModule调用
 *   * 通过 invoke 方法进入意图处理流程，意图经 模块 到 控制器（控制器启用流水线）处理
 *   * 控制器不共享,模型共享
 *
 */
class Module
{
    /**
     * 索引-列表-模型定义
     */
    const string KEY_MODELS = 'models';
    /**
     * 索引-列表-控制器定义
     */
    const string KEY_CONTROLLERS = 'controllers';
    /**
     * 索引-列表-动作定义
     */
    const string KEY_ACTIONS = 'actions';
    /**
     * 索引-字符串-默认控制器类
     */
    const string KEY_DEFAULT_CTRL_CLASS = 'controller';
    /**
     * 信息-错误
     */
    const string KEY_NO_CONTROLLER = '未找到控制器${name}';

    /**
     * 和Kernel级联配置
     */
    use Configurable;

    public string $label {
        get {
            return $this->config->get('label', $this->name);
        }
    }

    /**
     * @var ?string 模块路径
     */
    protected(set) ?string $dir = null {
        get {
            return $this->dir;
        }
    }

    /**
     * @var ?string 命名空间
     */
    protected ?string $namespace = null;

    /**
     * @var ?string 视图路径
     */
    protected(set) ?string $vir = null {
        get {
            if ($this->vir === null)
                $this->vir = $this->dir . DIRECTORY_SEPARATOR . 'views';

            return $this->vir;
        }
    }

    /**
     * @var NameRecorder|null 日志记录器
     */
    protected(set) ?NameRecorder $recorder = null {
        get {
            if (!$this->recorder) $this->recorder = new NameRecorder($this->kernel->Logger(), $this->label);
            return $this->recorder;
        }
    }

    public array $schema {
        get {
            return $this->config->get(self::KEY_MODELS, []);
        }
    }

    /**
     * @var ClosureCollection<Controller>|null
     */
    private ?ClosureCollection $controllers = null {
        get {
            if (!$this->controllers) {
                //初始化模块管理器
                $this->controllers = new ClosureCollection(
                    fn ($name) => $this->_try_get_controller($name),
                );
            }
            return $this->controllers;
        }
    }

    public function __construct(#[Dependent] protected(set) Kernel $kernel {
        get {
            return $this->kernel;
        }
    }, protected(set) string $name {
        get {
            return $this->name;
        }
    })
    {
        // 构建模块实际路径
        if ($this->dir === null) {
            try {
                $class = new ReflectionClass($this);
                $this->dir = dirname($class->getFileName());
            } catch (Throwable) {
            }
        }

        // 构建模块命名空间
        if ($this->namespace === null) $this->namespace = class_namespace($this);

        // 语言注入
        $this->kernel->Language()->compile($this->dir);

        // 级联配置
        $this->config = $this->kernel->config->branch(
            new Accessor(Obj::getInstance(), safe_file($this->dir, CONFIG_JSON, '.json'))
        );
    }

    /**
     * ## 获取$controller[.$action]的全部配置
     *
     * @param string $controller
     * @param string|null $action
     * @param mixed $default
     * @return mixed
     */
    public function getConfigOf(string $controller, ?string $action = null, mixed $default = null): mixed
    {
        $actions = Module::KEY_ACTIONS;
        $controllers = Module::KEY_CONTROLLERS;
        return $this->config->get(
            $action ? "$controllers.$controller.$actions.$action" : "$controllers.$controller",
            $default,
            false,
        );
    }

    /**
     * ## 执行意图
     *   * intent[class-name]: 启用通用控制器
     *
     * @param Request $request
     *
     * @return void
     * @throws Throwable
     */
    public function invoke(Request $request): void
    {
        $this->controllers->get($request->section->controller)?->invoke($request);
    }

    /**
     * 根据类名创建控制器实例
     *
     * @param string $name
     * @param string $class
     *
     * @return Controller
     * @throws Throwable
     */
    private function _get_controller_instance(string $name, string $class): Controller
    {
        $arguments = [$this, $name];
        return $this->kernel->container?->createObject($class, $arguments, false);
    }

    /**
     * 猜测类名并创建控制器实例
     *
     * @param string $name
     *
     * @return Controller
     * @throws Throwable
     */
    private function _try_get_controller(string $name): Controller
    {
        $uc = ucfirst($name);
        $classes = [
            "$this->namespace\\Controller\\$uc",
            $this->config->get(self::KEY_CONTROLLERS . ".$name.class"),
            $this->config->get(self::KEY_DEFAULT_CTRL_CLASS),
        ];
        foreach ($classes as $class) {
            try {
                return $this->_get_controller_instance($name, $class);
            } catch (Throwable) {}
        }
        throw new Exception(self::KEY_NO_CONTROLLER, ['name' => $name]);
    }
}